﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;
using System.Reflection;
using System.IO;

namespace CallFlowDesigner
{
    public partial class cfTextBox : RichTextBox
    {

        Collection<string> changes = new Collection<string>();
        Collection<int> positions = new Collection<int>();

        string[] elements;
        string[] attributes;

        const short WM_PAINT = 0x00F;

        bool paint = true;
        bool suppressTextChangedEvent = false;

        
        public cfTextBox()
        {
            this.AcceptsTab = true;
            this.Multiline = true;
            InitializeComponent();
            
            Init();
        }

        public void Init()
		  {
			this.BackColor = Color.FromArgb(230, 255, 230);
            this.Text = Properties.Snippets.New;
            this.SelectionStart = 133;
            this.SelectionLength = 0;
            changes.Clear();
            positions.Clear();

            CallFlowValidator vd = new CallFlowValidator();
            elements = vd.ValidElements.ToArray();
            attributes = vd.ValidAttributes.ToArray();
        
        }

        private Point mouseDownPoint;

        protected override void OnMouseDown(MouseEventArgs e)
        {
            mouseDownPoint = e.Location;
            base.OnMouseDown(e);
        }

        public void Insert(string text)
        {
            int position = this.GetCharIndexFromPosition(mouseDownPoint);
            this.Text = this.Text.Insert(position, text);
            this.SelectionStart = position + text.Length;
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            //don't undo while the DEL or BACKSPACE keys are held down
            if (e.KeyCode == Keys.Back || e.KeyCode == Keys.Delete) disregardTextChanged = true;
            base.OnKeyDown(e);
        }


        protected override void OnKeyUp(KeyEventArgs e)
        {

            disregardTextChanged = false;

            if (e.KeyCode == Keys.Z && e.Modifiers == Keys.Control)
            {
                DoUndo();
                return;
            }
            base.OnKeyUp(e);
        }

        private void DoUndo()
        {
            if (changes.Count == 0) return;

            if (changes.Count == 1)
            {

                changes.Clear();
                positions.Clear();
                Init();
                return;
            }

            this.Text = changes[changes.Count - 2];
            this.SelectionStart = positions[changes.Count - 2];
            changes.RemoveAt(changes.Count - 1);
            positions.RemoveAt(positions.Count - 1);
            return;
        }

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            //capture CTRL+Z held down
            if ((byte)e.KeyChar == 26) DoUndo();

            base.OnKeyPress(e);
        }

        bool disregardTextChanged = false;
        protected override void OnTextChanged(EventArgs e)
        {
            if (!suppressTextChangedEvent)
            {
                ChangeText();
                base.OnTextChanged(e);
                bool logChange = true;

                if (!disregardTextChanged)
                {
                    if (changes.Count > 1 && this.Text == changes[changes.Count - 2]) logChange = false;

                    if (logChange)
                    {
                        changes.Add(this.Text);
                        positions.Add(this.SelectionStart);
                    }
                }
                this.ClearUndo();
            }
        }

        private string oldText = "";
		  private void ChangeText()
		  {
              suppressTextChangedEvent = true;
			  int lengthDiff = oldText.Length - Rtf.Length;
			  if ((lengthDiff * lengthDiff) > 1)
			  {
				  int selectionStart = SelectionStart;
				  int selectionLength = SelectionLength;
                  
				  Text = Text;

				  SelectionStart = selectionStart;
				  SelectionLength = selectionLength;
			  }

              HighlightSyntax();
			  oldText = this.Rtf;
              suppressTextChangedEvent = false;
		  }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_PAINT)
            {
                if (this.paint) base.WndProc(ref m);
                else m.Result = IntPtr.Zero;
            }
            else
            {
                base.WndProc(ref m);
            }
        }
        
        public void HighlightSyntax()
        {
            bool suppress = suppressTextChangedEvent;
            suppressTextChangedEvent = true;
            int i = this.SelectionStart;
            int j = this.SelectionLength;

            this.paint = false;
            this.SelectAll();
            this.SelectionColor = Color.Black;

            foreach (string elemName in elements)
            {
                HighlightElementName(elemName, Properties.SyntaxHighlighting.Default.ElementColor);
            }
            foreach (string attributeName in attributes)
            {
                HighlightAttributeName(attributeName, Properties.SyntaxHighlighting.Default.AttributeColor);
            }

            foreach (System.Configuration.SettingsProperty setting in Properties.SyntaxHighlighting.Default.Properties)
            {
                if (setting.Name.EndsWith("Regex"))
                {
                    string regex = (String)setting.DefaultValue;
                    Color col = (Color)Properties.SyntaxHighlighting.Default[setting.Name.Replace("Regex", "Color")];
                    Highlight(regex, col);
                }
            }
            

            this.Select(i, j);
            this.paint = true;
         
            suppressTextChangedEvent = suppress;
        }


        

        private void HighlightElementName(string elemName, Color col)
        {
            string regex = "< *[/]?[ ]*(" + elemName + ")[ *>?]";
            Highlight(regex, col);
        }

        private void HighlightAttributeName(string attribName, Color col)
        {
            string regex = "<.*(" + attribName + ")[ ]*=[ ]*[\"']";
            Highlight(regex, col);
        }

        private void Highlight(string regex, Color col)
        {
            MatchCollection mtchs = Regex.Matches(this.Text, regex);

            foreach (Match mtch in mtchs)
            {
                Group grp = mtch.Groups[1];

                int startIndex = grp.Index;
                int length = grp.Length;
                this.Select(startIndex, length);
                this.SelectionColor = col;
            }
        }

    }
}

